package com.wcs.exam.controller.biz;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wcs.exam.common.base.BaseBiz;
import com.wcs.exam.common.base.Result;
import com.wcs.exam.common.base.exception.BaseException;
import com.wcs.exam.common.base.page.SqlUtil;
import com.wcs.exam.common.constant.BaseConstant;
import com.wcs.exam.common.constant.RedisConstant;
import com.wcs.exam.common.enums.*;
import com.wcs.exam.common.llm.Llm;
import com.wcs.exam.common.llm.LlmService;
import com.wcs.exam.common.llm.req.AiQuestionGradingReq;
import com.wcs.exam.common.llm.resp.AiQuestionGradingResp;
import com.wcs.exam.common.util.BeanUtil;
import com.wcs.exam.controller.auth.resp.AuthExamEndResp;
import com.wcs.exam.controller.biz.resp.ExamCommonResp;
import com.wcs.exam.controller.biz.resp.QuestionDetailResp;
import com.wcs.exam.dao.*;
import com.wcs.exam.dao.impl.mapper.entity.*;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.*;

@Slf4j
@Component
@RequiredArgsConstructor
public class ExamCommonBiz extends BaseBiz {

    @NotNull
    private final RedissonClient redissonClient;

    @NotNull
    private final LlmService llmService;

    @NotNull
    private final QuestionCommonBiz questionCommonBiz;
    @NotNull
    private final SysConfigCommonBiz sysConfigCommonBiz;

    @NotNull
    private final ExamDao examDao;
    @NotNull
    private final ExamUserDao examUserDao;
    @NotNull
    private final ExamUserRecordDao examUserRecordDao;
    @NotNull
    private final ExamUserAnswerDao examUserAnswerDao;
    @NotNull
    private final PaperDao paperDao;
    @NotNull
    private final PaperTitleDao paperTitleDao;
    @NotNull
    private final PaperQuestionDao paperQuestionDao;

    public ExamCommonResp getExamDetail(Long paperId, Long recordId) {
        Paper paper = paperDao.getById(paperId);
        if (ObjectUtil.isNull(paper)) {
            throw new BaseException("试卷不存在");
        }
        return getExamDetail(paper, recordId);
    }


    public ExamCommonResp getExamDetail(Paper paper) {
        return getExamDetail(paper, null);
    }

    public ExamCommonResp getExamDetail(Paper paper, Long recordId) {
        // 试卷大题
        List<PaperTitle> titleList = paperTitleDao.listByPaperIdForCache(paper.getId());

        // 试卷题目
        Map<Long, List<PaperQuestion>> paperQuestionMap = new HashMap<>();
        Map<Long, QuestionDetailResp> questionMap = new HashMap<>();

        List<PaperQuestion> paperQuestionList = paperQuestionDao.listByPaperIdForCache(paper.getId());
        if (CollectionUtil.isNotEmpty(paperQuestionList)) {
            // 迭代大题下一级题目
            paperQuestionMap.putAll(paperQuestionList.stream()
                    .collect(Collectors.groupingBy(PaperQuestion::getTitleId))
            );


            // 获取题目信息
            Set<Long> questionIds = paperQuestionList.stream()
                    .map(PaperQuestion::getQuestionId)
                    .collect(Collectors.toSet());

            List<QuestionDetailResp> questionList = questionCommonBiz.listDetailByIds(questionIds);
            if (CollectionUtil.isNotEmpty(questionList)) {
                questionList.forEach(item -> questionMap.put(item.getId(), item));
            }
        }

        // 获取考试答案
        Map<Long, List<ExamUserAnswer>> examUserAnswerMap = new HashMap<>();
        if (ObjectUtil.isNotNull(recordId)) {
            List<ExamUserAnswer> examUserAnswerList = examUserAnswerDao.lambdaQuery()
                    .eq(ExamUserAnswer::getRecordId, recordId)
                    .orderByAsc(ExamUserAnswer::getSort).list();
            if (CollectionUtil.isNotEmpty(examUserAnswerList)) {
                examUserAnswerMap.putAll(
                        examUserAnswerList.stream()
                                .collect(Collectors.groupingBy(ExamUserAnswer::getTitleId))
                );
            }
        }


        ExamCommonResp resp = new ExamCommonResp();
        resp.setQuestionCount(paper.getQuestionCount());
        resp.setTotalScore(paper.getTotalScore());
        resp.setPassScore(paper.getPassScore());

        // 处理大题
        List<ExamCommonResp.ExamTitle> respTitleList = new ArrayList<>();
        for (PaperTitle paperTitle : titleList) {
            ExamCommonResp.ExamTitle respTitle = new ExamCommonResp.ExamTitle();
            respTitle.setId(paperTitle.getId());
            respTitle.setTitleName(paperTitle.getTitleName());
            respTitle.setTitleScore(paperTitle.getTitleScore());
            respTitle.setSort(paperTitle.getSort());

            List<ExamCommonResp.Question> respQuestionList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(examUserAnswerMap)) {
                handleExistUserAnswerTitleQuestion(paperTitle, examUserAnswerMap, paperQuestionMap, questionMap, respQuestionList);
            } else {
                handleNotExistUserAnswerTitleQuestion(paper, paperTitle, paperQuestionMap, questionMap, respQuestionList);
            }

            respTitle.setQuestionCount(respQuestionList.size());
            respTitle.setQuestions(respQuestionList);
            respTitleList.add(respTitle);
        }


        resp.setTitles(respTitleList);
        return resp;
    }

    /**
     * 处理标题下存在用户答案的题目
     *
     * @param paperTitle        试卷标题
     * @param examUserAnswerMap 考试用户答案集合
     * @param paperQuestionMap  试卷题目集合
     * @param questionMap       题目信息集合
     * @param respQuestionList  响应题目集合
     */
    private static void handleExistUserAnswerTitleQuestion(
            PaperTitle paperTitle,
            Map<Long, List<ExamUserAnswer>> examUserAnswerMap,
            Map<Long, List<PaperQuestion>> paperQuestionMap,
            Map<Long, QuestionDetailResp> questionMap,
            List<ExamCommonResp.Question> respQuestionList
    ) {
        List<ExamUserAnswer> titleUserAnswerList = examUserAnswerMap.get(paperTitle.getId());
        if (CollectionUtil.isNotEmpty(titleUserAnswerList)) {
            List<PaperQuestion> titleQuestionList = paperQuestionMap.get(paperTitle.getId());
            Map<Long, PaperQuestion> titleQuestionMap = new HashMap<>();
            if (CollectionUtil.isNotEmpty(titleQuestionList)) {
                titleQuestionList.forEach(item -> titleQuestionMap.put(item.getQuestionId(), item));
            }


            for (ExamUserAnswer examUserAnswer : titleUserAnswerList) {
                QuestionDetailResp question = questionMap.get(examUserAnswer.getQuestionId());
                if (ObjectUtil.isNull(question)) {
                    continue;
                }

                ExamCommonResp.Question respQuestion = new ExamCommonResp.Question()
                        .setQuestionId(question.getId())
                        .setQuestionType(question.getQuestionType())
                        .setQuestionScore(examUserAnswer.getQuestionScore())
                        .setSort(examUserAnswer.getSort())
                        .setDifficultyLevel(question.getDifficultyLevel())
                        .setQuestionTitle(question.getQuestionTitle())
                        .setOptionCount(0)
                        .setOptions(Collections.emptyList())
                        .setUserAnswer(examUserAnswer.getUserAnswer());

                PaperQuestion paperQuestion = titleQuestionMap.get(examUserAnswer.getQuestionId());
                if (ObjectUtil.isNotNull(paperQuestion)) {
                    respQuestion.setMultiScoreWay(paperQuestion.getMultiScoreWay())
                            .setMultiScore(paperQuestion.getMultiScore())
                            .setFillScoreWay(paperQuestion.getFillScoreWay())
                            .setFillScore(paperQuestion.getFillScore());
                }

                if (QuestionTypeEnum.isExistOption(question.getQuestionType())) {
                    // 存在选项
                    List<ExamCommonResp.QuestionOption> respOptions = BeanUtil.copyProperties(question.getOptions(), ExamCommonResp.QuestionOption.class);
                    respQuestion.setOptions(respOptions);
                }
                if (QuestionTypeEnum.GAP_FILLING.getCode().equals(question.getQuestionType())) {
                    // 填空题
                    respQuestion.setOptionCount(question.getOptionCount());
                }
                respQuestionList.add(respQuestion);
            }
        }
    }

    /**
     * 处理标题下不存在用户答案的题目
     *
     * @param paper            试卷信息
     * @param paperTitle       标题信息
     * @param paperQuestionMap 试卷题目集合
     * @param questionMap      题目集合
     * @param respQuestionList 响应题目信息集合
     */
    private static void handleNotExistUserAnswerTitleQuestion(
            Paper paper,
            PaperTitle paperTitle,
            Map<Long, List<PaperQuestion>> paperQuestionMap,
            Map<Long, QuestionDetailResp> questionMap,
            List<ExamCommonResp.Question> respQuestionList) {
        List<PaperQuestion> titleQuestionList = paperQuestionMap.get(paperTitle.getId());
        if (CollectionUtil.isNotEmpty(titleQuestionList)) {
            for (PaperQuestion paperQuestion : titleQuestionList) {
                QuestionDetailResp question = questionMap.get(paperQuestion.getQuestionId());
                if (ObjectUtil.isNull(question)) {
                    continue;
                }

                ExamCommonResp.Question respQuestion = new ExamCommonResp.Question()
                        .setQuestionId(question.getId())
                        .setQuestionType(question.getQuestionType())
                        .setQuestionScore(paperQuestion.getQuestionScore())
                        .setMultiScoreWay(paperQuestion.getMultiScoreWay())
                        .setMultiScore(paperQuestion.getMultiScore())
                        .setFillScoreWay(paperQuestion.getFillScoreWay())
                        .setFillScore(paperQuestion.getFillScore())
                        .setSort(paperQuestion.getSort())
                        .setDifficultyLevel(question.getDifficultyLevel())
                        .setQuestionTitle(question.getQuestionTitle())
                        .setOptionCount(0)
                        .setOptions(Collections.emptyList())
                        .setUserAnswer("");

                if (QuestionTypeEnum.isExistOption(question.getQuestionType())) {
                    // 存在选项
                    List<ExamCommonResp.QuestionOption> respOptions = BeanUtil.copyProperties(question.getOptions(), ExamCommonResp.QuestionOption.class);
                    respQuestion.setOptions(respOptions);
                }
                if (QuestionTypeEnum.GAP_FILLING.getCode().equals(question.getQuestionType())) {
                    // 填空题
                    respQuestion.setOptionCount(question.getOptionCount());
                }
                respQuestionList.add(respQuestion);
            }

            // 随机组卷
            handleRandomCombine(paper, paperTitle, respQuestionList);

            // 乱序
            handleDerangement(paper, respQuestionList);
        }
    }

    /**
     * 随机组卷
     *
     * @param paper            试卷信息
     * @param paperTitle       试卷大题
     * @param respQuestionList 试卷题目列表
     */
    private static void handleRandomCombine(Paper paper, PaperTitle paperTitle, List<ExamCommonResp.Question> respQuestionList) {
        if (!Boolean.TRUE.equals(paper.getRandomCombine())) {
            return;
        }
        List<ExamCommonResp.Question> respRandomQuestionList = new ArrayList<>();

        // 根据题型分组汇总
        Map<Integer, List<ExamCommonResp.Question>> typeQuestionMap = respQuestionList.stream().collect(Collectors.groupingBy(ExamCommonResp.Question::getQuestionType));

        // 单选题
        getRandomCombineByType(respRandomQuestionList, typeQuestionMap, QuestionTypeEnum.SINGLE_CHOICE.getCode(), paperTitle.getSingleChoiceCount());
        // 多选题
        getRandomCombineByType(respRandomQuestionList, typeQuestionMap, QuestionTypeEnum.MULTIPLE_CHOICE.getCode(), paperTitle.getMultipleChoiceCount());
        // 判断题
        getRandomCombineByType(respRandomQuestionList, typeQuestionMap, QuestionTypeEnum.JUDGMENT.getCode(), paperTitle.getJudgmentCount());
        // 填空题
        getRandomCombineByType(respRandomQuestionList, typeQuestionMap, QuestionTypeEnum.GAP_FILLING.getCode(), paperTitle.getGapFillingCount());
        // 简答题
        getRandomCombineByType(respRandomQuestionList, typeQuestionMap, QuestionTypeEnum.SHORT_ANSWER.getCode(), paperTitle.getShortAnswerCount());

        respQuestionList.clear();
        respQuestionList.addAll(respRandomQuestionList);
    }

    /**
     * 获取随机组卷
     *
     * @param respList        响应试题列表
     * @param typeQuestionMap 题目信息集合
     * @param questionType    题目类型
     * @param questionCount   题目数量
     */
    private static void getRandomCombineByType(List<ExamCommonResp.Question> respList, Map<Integer, List<ExamCommonResp.Question>> typeQuestionMap, Integer questionType, Integer questionCount) {
        if (questionCount <= 0 || !typeQuestionMap.containsKey(questionType)) {
            return;
        }

        List<ExamCommonResp.Question> targetQuestions = typeQuestionMap.get(questionType);
        respList.addAll(targetQuestions.size() > questionCount ? targetQuestions.subList(0, questionCount) : targetQuestions);
    }

    /**
     * 处理乱序
     *
     * @param paper            试卷信息
     * @param respQuestionList 响应试题列表
     */
    private static void handleDerangement(Paper paper, List<ExamCommonResp.Question> respQuestionList) {
        if (!Boolean.TRUE.equals(paper.getDerangement())) {
            return;
        }

        // 题目乱序
        if (Boolean.TRUE.equals(paper.getQuestionDerangement())) {
            Collections.shuffle(respQuestionList);
        }

        // 选项乱序
        if (Boolean.TRUE.equals(paper.getOptionDerangement())) {
            for (ExamCommonResp.Question question : respQuestionList) {
                if (QuestionTypeEnum.isExistOption(question.getQuestionType())) {
                    Collections.shuffle(question.getOptions());
                }
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public Result<AuthExamEndResp> handleFinishExam(ExamUserRecord examUserRecord) {
        return handleFinishExam(examUserRecord.getPaperId(), examUserRecord);
    }

    /**
     * 处理考试记录
     *
     * @param examUserRecord 考试记录
     * @return 处理后的参数
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<AuthExamEndResp> handleFinishExam(Long paperId, ExamUserRecord examUserRecord) {
        // 获取用户答案
        List<ExamUserAnswer> answerList = examUserAnswerDao.listByRecordId(examUserRecord.getId());
        Map<Long, List<ExamUserAnswer>> titleAnswerMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(answerList)) {
            titleAnswerMap.putAll(answerList.stream().collect(groupingBy(ExamUserAnswer::getTitleId)));
        }

        // 获取试卷试题、题目信息、题目选项
        Map<Long, List<PaperQuestion>> paperQuestionMap = new HashMap<>();
        Map<Long, QuestionDetailResp> questionMap = new HashMap<>();

        // 获取试卷试题
        List<PaperQuestion> paperQuestionList = paperQuestionDao.listByPaperIdForCache(paperId);
        if (CollectionUtil.isNotEmpty(paperQuestionList)) {
            paperQuestionMap.putAll(paperQuestionList.stream().collect(groupingBy(PaperQuestion::getTitleId)));

            // 获取题目信息
            List<QuestionDetailResp> questionList = cacheRedis.list(RedisConstant.Exam.PAPER_QUESTION_DETAIL + paperId, QuestionDetailResp.class);
            if (CollectionUtil.isEmpty(questionList)) {
                Set<Long> questionIds = paperQuestionList.stream().map(PaperQuestion::getQuestionId).collect(toSet());
                questionList = questionCommonBiz.listDetailByIds(questionIds);
                if (CollectionUtil.isNotEmpty(questionList)) {
                    questionList.forEach(item -> questionMap.put(item.getId(), item));

                    cacheRedis.set(RedisConstant.Exam.PAPER_QUESTION_DETAIL + paperId, questionList, 10, TimeUnit.SECONDS);
                }
            }
        }

        // 处理答案评阅(处理答案批阅、以及批阅后的各个考试类型数据的更新)
        AuthExamEndResp resp = handleAnswerCorrect(paperQuestionMap, questionMap, examUserRecord, titleAnswerMap);
        resp.setRecordId(examUserRecord.getId());
        resp.setEvaluationMethod(examUserRecord.getEvaluationMethod());
        resp.setTotalScore(examUserRecord.getTotalScore());

        // 获取历史最高分
        BigDecimal historyHighestScore = getHighestScore(examUserRecord);
        resp.setHistoryHighestScore(historyHighestScore);

        // 显示答案规则
        ExamShowRule answerRule = getShowAnswerRule(examUserRecord);
        if (ObjectUtil.isNotNull(answerRule)) {
            resp.setResultShow(answerRule.getResultShow());
            resp.setResultShowRule(answerRule.getResultShowRule());
            resp.setResultShowTime(answerRule.getResultShowTime());
            resp.setAnswerShow(answerRule.getAnswerShow());
            resp.setAnswerShowRule(answerRule.getAnswerShowRule());
            resp.setResultShowRank(answerRule.getResultShowRank());
            resp.setRankList(answerRule.getRankList());

            // 排名
            if (Boolean.TRUE.equals(answerRule.getResultShowRank())) {
                Integer rankNo = examUserDao.getRankNoByExamTypeAndExamIdAndUserId(examUserRecord.getExamType(), examUserRecord.getRelateId(), examUserRecord.getUserId());
                resp.setRankNo(rankNo);
            }

            //判断分数显示规则
            if (ObjectUtil.isNotNull(resp.getResultShow())) {
                boolean checkRes = Boolean.TRUE;
                if (Boolean.FALSE.equals(resp.getResultShow())) {
                    checkRes = Boolean.FALSE;
                } else {
                    if (ResultShowRuleEnum.END.getCode().equals(resp.getResultShowRule())) {
                        checkRes = LocalDateTime.now().isAfter(examUserRecord.getEndTime());
                    } else if (ResultShowRuleEnum.SUBMIT.getCode().equals(resp.getResultShowRule())) {
                        checkRes = Boolean.TRUE;
                    } else if (ResultShowRuleEnum.CUSTOM.getCode().equals(resp.getResultShowRule())) {
                        checkRes = LocalDateTime.now().isAfter(resp.getResultShowTime());
                    }
                }

                //不显示分数或者未到达显示时间，则把对应分数设置为0
                if (!checkRes) {
                    resp.setUserScore(null);
                    resp.setHistoryHighestScore(BigDecimal.ZERO);
                    resp.setSubjectiveQuestionScore(BigDecimal.ZERO);
                    resp.setObjectiveQuestionScore(BigDecimal.ZERO);
                }
            }
        }
        // 校验是否可以继续考试
        ExamCheckContinue examCheckContinue = checkCanContinueExam(examUserRecord.getUserId(), examUserRecord.getExamType(), examUserRecord.getRelateId());
        resp.setStartExam(examCheckContinue.getStartExam());
        resp.setResidueExamCount(examCheckContinue.getResidueExamCount());
        return Result.success(resp);
    }

    /**
     * 处理答案评阅
     *
     * @param examUserRecord        考试记录
     * @param titleAnswerMap        用户答案
     * @param titlePaperQuestionMap 试卷试题
     * @param questionMap           题目信息
     * @return 处理结果
     */
    private AuthExamEndResp handleAnswerCorrect(
            Map<Long, List<PaperQuestion>> titlePaperQuestionMap,
            Map<Long, QuestionDetailResp> questionMap,
            ExamUserRecord examUserRecord,
            Map<Long, List<ExamUserAnswer>> titleAnswerMap
    ) {
        // 计算考试用时
        if (examUserRecord.getEndTime().isBefore(LocalDateTime.now())) {
            // 当前时间大于结束时间
            long seconds = Duration.between(examUserRecord.getGmtCreate(), examUserRecord.getEndTime()).getSeconds();
            examUserRecord.setUserAnswerTime((int) seconds);
        } else {
            // 当前时间小于结束时间
            examUserRecord.setSubmitTime(LocalDateTime.now());
            long seconds = Duration.between(examUserRecord.getGmtCreate(), examUserRecord.getSubmitTime()).getSeconds();
            examUserRecord.setUserAnswerTime((int) seconds);
        }

        // 判断如果是 自动评阅 就是 批改完成状态否则就是 待批改状态
        examUserRecord.setExamStatus(EvaluationMethodEnum.AUTO.getCode().equals(examUserRecord.getEvaluationMethod()) ? ExamStatusEnum.COMPLETE.getCode() : ExamStatusEnum.WAIT_CORRECTED.getCode());

        // 获取试卷信息
        Exam exam = getExam(examUserRecord);

        // 评阅
        ExamEvaluationResult evaluationResult = handleUserAnswerEvaluation(exam, titlePaperQuestionMap, titleAnswerMap, questionMap);

        // 处理评分结果
        examUserRecord.setUserScore(evaluationResult.getUserScore());

        Integer passStatus;
        if (EvaluationMethodEnum.ARTIFICIAL.getCode().equals(examUserRecord.getEvaluationMethod())) {
            passStatus = PassStatusEnum.UN_KNOWN.getCode();
        } else {
            passStatus = examUserRecord.getUserScore().compareTo(examUserRecord.getPassScore()) >= 0 ? PassStatusEnum.PASS.getCode() : PassStatusEnum.FAIL.getCode();
        }
        examUserRecord.setPassStatus(passStatus);

        // 更新考试记录
        examUserRecordDao.updateById(examUserRecord);

        // 更新用户答案
        if (CollectionUtil.isNotEmpty(evaluationResult.getUpdateAnswerList())) {
            examUserAnswerDao.updateBatchById(evaluationResult.getUpdateAnswerList());
        }

        if (ExamTypeEnum.MOCK.getCode().equals(examUserRecord.getExamType()) || ExamTypeEnum.EXAMINATION.getCode().equals(examUserRecord.getExamType())) {
            // 模拟考试、正式考试用户关联数据
            handleExamUser(examUserRecord);
        }

        // 创建响应对象
        return new AuthExamEndResp()
                .setUserAnswerTime(examUserRecord.getUserAnswerTime())
                .setUserScore(examUserRecord.getUserScore())
                .setSubjectiveQuestionScore(evaluationResult.getSubjectiveQuestionScore())
                .setObjectiveQuestionScore(evaluationResult.getObjectiveQuestionScore())
                .setQuestionCorrectCount(evaluationResult.getCorrectQuestionCount())
                .setQuestionErrorCount(evaluationResult.getQuestionCount() - evaluationResult.getCorrectQuestionCount())
                .setPassStatus(examUserRecord.getPassStatus());
    }

    /**
     * 处理用户答案评阅
     *
     * @param titlePaperQuestionMap 大题试卷试题集合
     * @param titleAnswerMap        大题用户答案集合
     * @param questionMap           试题集合
     * @return 处理结果
     */
    private ExamEvaluationResult handleUserAnswerEvaluation(
            Exam exam,
            Map<Long, List<PaperQuestion>> titlePaperQuestionMap,
            Map<Long, List<ExamUserAnswer>> titleAnswerMap,
            Map<Long, QuestionDetailResp> questionMap
    ) {
        // 主观题大题的题目数
        int questionCount = 0;
        // 正确题数
        Integer correctQuestionCount = 0;
        // 主观题得分
        BigDecimal subjectiveQuestionScore = BigDecimal.ZERO;
        // 客观题得分
        BigDecimal objectiveQuestionScore = BigDecimal.ZERO;
        // 待更新用户答案
        List<ExamUserAnswer> updateAnswerList = new ArrayList<>();

        // 调用大模型接口
        Llm llm = sysConfigCommonBiz.getSysConfig(Llm.class);

        // 评阅
        for (Map.Entry<Long, List<ExamUserAnswer>> answerEntry : titleAnswerMap.entrySet()) {
            // 获取大题的题目
            List<PaperQuestion> paperQuestionList = titlePaperQuestionMap.get(answerEntry.getKey());
            if (CollectionUtil.isEmpty(paperQuestionList)) {
                continue;
            }

            Map<Long, PaperQuestion> onePaperQuestionMap = paperQuestionList.stream().collect(toMap(PaperQuestion::getQuestionId, item -> item));


            for (ExamUserAnswer answer : answerEntry.getValue()) {
                // 获取试卷试题
                PaperQuestion paperQuestion = onePaperQuestionMap.get(answer.getQuestionId());
                if (ObjectUtil.isNull(paperQuestion)) {
                    updateAnswerList.add(createUpdateAnswer(answer.getId(), BigDecimal.ZERO, Boolean.TRUE));
                    continue;
                }

                // 获取题目信息
                QuestionDetailResp question = questionMap.get(answer.getQuestionId());
                if (ObjectUtil.isNull(question)) {
                    updateAnswerList.add(createUpdateAnswer(answer.getId(), BigDecimal.ZERO, Boolean.TRUE));
                    continue;
                }

                if (!QuestionTypeEnum.SHORT_ANSWER.getCode().equals(question.getQuestionType())) {
                    questionCount++;
                }

                // 评分
                if (QuestionTypeEnum.SINGLE_CHOICE.getCode().equals(question.getQuestionType())) {
                    // 单选题
                    boolean checkResult = checkSingleChoiceAnswer(question, answer.getUserAnswer());
                    updateAnswerList.add(createUpdateAnswer(answer.getId(), checkResult ? paperQuestion.getQuestionScore() : BigDecimal.ZERO, Boolean.TRUE));
                    if (checkResult) {
                        objectiveQuestionScore = objectiveQuestionScore.add(paperQuestion.getQuestionScore());
                        correctQuestionCount++;
                    }
                } else if (QuestionTypeEnum.MULTIPLE_CHOICE.getCode().equals(question.getQuestionType())) {
                    // 多选题
                    BigDecimal score = checkMultipleChoiceAnswer(paperQuestion, question, answer.getUserAnswer());
                    updateAnswerList.add(createUpdateAnswer(answer.getId(), score, Boolean.TRUE));
                    if (BigDecimal.ZERO.compareTo(score) < 0) {
                        objectiveQuestionScore = objectiveQuestionScore.add(score);
                        correctQuestionCount++;
                    }
                } else if (QuestionTypeEnum.JUDGMENT.getCode().equals(question.getQuestionType())) {
                    // 判断题
                    boolean checkResult = question.getQuestionAnswer().equals(answer.getUserAnswer());
                    updateAnswerList.add(createUpdateAnswer(answer.getId(), checkResult ? answer.getQuestionScore() : BigDecimal.ZERO, Boolean.TRUE));
                    if (checkResult) {
                        objectiveQuestionScore = objectiveQuestionScore.add(paperQuestion.getQuestionScore());
                        correctQuestionCount++;
                    }
                } else {
                    if (StrUtil.isBlank(answer.getUserAnswer())) {
                        // 用户答案为空
                        ExamUserAnswer updateUserAnswer = new ExamUserAnswer()
                                .setId(answer.getId())
                                .setUserScore(BigDecimal.ZERO)
                                .setEvaluationComplete(Boolean.TRUE);
                        updateAnswerList.add(updateUserAnswer);
                    } else {
                        try {
                            if (exam.getAiGrading()) {
                                // 开启AI评阅
                                AiQuestionGradingReq gradingReq = new AiQuestionGradingReq()
                                        .setQuestionType(question.getQuestionType())
                                        .setQuestionTitle(question.getQuestionTitle())
                                        .setQuestionAnswer(question.getQuestionAnswer())
                                        .setQuestionScore(paperQuestion.getQuestionScore())
                                        .setOptionCount(question.getOptionCount())
                                        .setUserAnswer(answer.getUserAnswer());
                                AiQuestionGradingResp gradingResp = llmService.questionGrading(llm, gradingReq);
                                if (gradingResp != null) {
                                    ExamUserAnswer updateUserAnswer = new ExamUserAnswer()
                                            .setId(answer.getId())
                                            .setUserScore(gradingResp.getUserScore())
                                            .setEvaluationComplete(Boolean.TRUE)
                                            .setAiGrading(Boolean.TRUE)
                                            .setAiUserScore(gradingResp.getUserScore())
                                            .setAiGradingReason(gradingResp.getReason());
                                    updateAnswerList.add(updateUserAnswer);
                                    if (BigDecimal.ZERO.compareTo(gradingResp.getUserScore()) < 0) {
                                        objectiveQuestionScore = objectiveQuestionScore.add(gradingResp.getUserScore());
                                        correctQuestionCount++;
                                    }
                                }

                            }
                        } catch (Exception e) {
                            log.error("考试记录：{} - 题目ID：{} -  题目：{} - AI评阅失败，不影响原业务流程！", answer.getRecordId(), question.getId(), question.getQuestionTitle(), e);
                        }
                    }
                }
            }
        }

        BigDecimal userScore = objectiveQuestionScore.add(subjectiveQuestionScore);
        return new ExamEvaluationResult(
                questionCount,
                correctQuestionCount,
                userScore,
                objectiveQuestionScore,
                subjectiveQuestionScore,
                updateAnswerList
        );
    }

    /**
     * 考试用户关联数据
     *
     * @param examUserRecord 考试记录
     */
    public void handleExamUser(ExamUserRecord examUserRecord) {
        RLock lock = redissonClient.getLock(RedisConstant.Exam.EXAM_USER_HANDLER + examUserRecord.getUserId() + RedisConstant.SEPARATOR + examUserRecord.getRelateId());
        try {
            boolean res = lock.tryLock(RedisConstant.DEFAULT_WAIT_TIME, RedisConstant.DEFAULT_LEASE_TIME, TimeUnit.SECONDS);
            if (!res) {
                throw new BaseException("等待超时");
            }

            // 考试信息
            Exam exam = examDao.getById(examUserRecord.getRelateId());
            if (ObjectUtil.isNull(exam)) {
                throw new BaseException(this.toI18n("resource.exam.not.exist"));
            }
            ExamUser examUser = examUserDao.getByExamIdAndUserId(examUserRecord.getRelateId(), examUserRecord.getUserId());
            if (ObjectUtil.isNull(examUser)) {
                throw new BaseException(this.toI18n("resource.exam.user.not.exist"));
            }

            // 统计最高分
            ExamUserRecord highestScoreRecord = examUserRecordDao.lambdaQuery()
                    .eq(ExamUserRecord::getUserId, examUserRecord.getUserId())
                    .eq(ExamUserRecord::getExamType, examUserRecord.getExamType())
                    .eq(ExamUserRecord::getRelateId, examUserRecord.getRelateId())
                    .orderByDesc(ExamUserRecord::getUserScore)
                    .orderByAsc(ExamUserRecord::getId)
                    .last(SqlUtil.limitOne())
                    .one();

            // 最新得分
            ExamUserRecord newestScoreRecord = examUserRecordDao.lambdaQuery()
                    .eq(ExamUserRecord::getUserId, examUserRecord.getUserId())
                    .eq(ExamUserRecord::getExamType, examUserRecord.getExamType())
                    .eq(ExamUserRecord::getRelateId, examUserRecord.getRelateId())
                    .orderByDesc(ExamUserRecord::getId)
                    .last(SqlUtil.limitOne())
                    .one();

            ExamUserRecord userRecord;
            if (ExamScoreTypeEnum.HIGHEST.getCode().equals(exam.getExamScoreType())) {
                userRecord = highestScoreRecord;
            } else {
                userRecord = newestScoreRecord;
            }

            ExamUser updateExamUser = new ExamUser();
            updateExamUser.setId(examUser.getId());
            updateExamUser.setExamStartTime(userRecord.getGmtCreate());
            updateExamUser.setExamEndTime(userRecord.getEndTime());
            updateExamUser.setExamSubmitTime(userRecord.getSubmitTime());
            updateExamUser.setUserAnswerTime(userRecord.getUserAnswerTime());
            updateExamUser.setUserScore(userRecord.getUserScore());
            updateExamUser.setExamRecordId(userRecord.getId());
            if (userRecord.getExamStatus().equals(ExamStatusEnum.NOT_OVER.getCode())) {
                updateExamUser.setUserExamStatus(UserExamStatusEnum.DURING_EXAM.getCode());
            }
            updateExamUser.setUserHighestScore(highestScoreRecord.getUserScore());
            updateExamUser.setUserNewestScore(newestScoreRecord.getUserScore());

            if (exam.getEvaluationMethod().equals(EvaluationMethodEnum.AUTO.getCode())) {
                // 自动评阅
                if (examUserRecord.getExamStatus().equals(ExamStatusEnum.COMPLETE.getCode())) {
                    // 考试完成
                    updateExamUser.setUserExamStatus(examUserRecord.getPassStatus());
                }
                if (examUser.getUserEvaluationStatus().equals(UserEvaluationStatusEnum.REVIEWING_PAPER.getCode())) {
                    // 阅卷状态
                    updateExamUser.setUserEvaluationStatus(UserEvaluationStatusEnum.MARKING_COMPLETE.getCode());
                }
            } else {
                // 人工评阅
                if (examUserRecord.getExamStatus().equals(ExamStatusEnum.WAIT_CORRECTED.getCode())) {
                    // 考试完成
                    updateExamUser.setUserExamStatus(UserExamStatusEnum.EXAM_COMPLETE.getCode());
                    updateExamUser.setUserEvaluationStatus(UserEvaluationStatusEnum.WAIT_MARKED.getCode());
                }
                if (examUserRecord.getExamStatus().equals(ExamStatusEnum.COMPLETE.getCode())) {
                    updateExamUser.setUserExamStatus(examUserRecord.getPassStatus());
                    updateExamUser.setUserEvaluationStatus(UserEvaluationStatusEnum.MARKING_COMPLETE.getCode());
                }
            }
            examUserDao.updateById(updateExamUser);
        } catch (InterruptedException e) {
            log.error("======处理用户考试流程缓存锁失败================", e);
            Thread.currentThread().interrupt();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }

    /**
     * 创建用户答案更新对象
     *
     * @param id                 用户答案ID
     * @param userScore          用户得分
     * @param evaluationComplete 批改状态
     * @return 用户答案更新对象
     */
    public ExamUserAnswer createUpdateAnswer(Long id, BigDecimal userScore, Boolean evaluationComplete) {
        return new ExamUserAnswer()
                .setId(id)
                .setUserScore(userScore)
                .setEvaluationComplete(evaluationComplete);
    }

    /**
     * 校验单选题答案是否正确
     *
     * @param question   题目信息
     * @param userAnswer 用户答案
     * @return 校验结果
     */
    public boolean checkSingleChoiceAnswer(QuestionDetailResp question, String userAnswer) {
        if (ObjectUtil.isNull(question) || CollectionUtil.isEmpty(question.getOptions())) {
            return false;
        }
        if (StrUtil.isBlank(userAnswer)) {
            return false;
        }

        Map<String, QuestionDetailResp.Option> optionMap = question.getOptions().stream()
                .collect(toMap(QuestionDetailResp.Option::getOptionContent, Function.identity(), (first, second) -> first));
        QuestionDetailResp.Option option = optionMap.get(userAnswer);
        if (ObjectUtil.isNull(option)) {
            return false;
        }
        return option.getOptionContent().equals(question.getQuestionAnswer());
    }

    /**
     * 校验多选题答案是否正确
     *
     * @param paperQuestion 试题题目
     * @param question      题目信息
     * @param userAnswer    用户答案
     * @return 题目得分
     */
    public BigDecimal checkMultipleChoiceAnswer(PaperQuestion paperQuestion, QuestionDetailResp question, String userAnswer) {
        // 答案为空直接返回0分
        if (StrUtil.isBlank(userAnswer)) {
            return BigDecimal.ZERO;
        }
        // 用户答案
        List<String> userAnswers = Arrays.stream(userAnswer.split(BaseConstant.GET_SEPARATOR)).toList();

        // 题目选项为空
        if (CollectionUtil.isEmpty(question.getOptions())) {
            return BigDecimal.ZERO;
        }
        // 选项ID,选项
        Map<String, QuestionDetailResp.Option> optionMap = question.getOptions().stream()
                .collect(toMap(QuestionDetailResp.Option::getOptionContent, Function.identity()));

        List<String> userAnswerList = new ArrayList<>();
        for (String answer : userAnswers) {
            QuestionDetailResp.Option option = optionMap.get(answer);
            if (ObjectUtil.isNotNull(option)) {
                userAnswerList.add(option.getOptionContent());
            }
        }
        HashSet<String> userAnswerSet = new HashSet<>(userAnswerList);

        // 题目答案
        List<String> questionAnswerList = Arrays.asList(question.getQuestionAnswer().split(BaseConstant.GET_SEPARATOR));
        // 如果用户的答案大于正确答案数，则表示用户答案中有错误选项分数0分
        if (questionAnswerList.size() < userAnswerList.size()) {
            return BigDecimal.ZERO;
        }
        HashSet<String> questionAnswerSet = new HashSet<>(questionAnswerList);

        // 用户答案和正确答案相同
        if (questionAnswerSet.containsAll(userAnswerSet)) {
            if (userAnswerList.size() == questionAnswerSet.size()) {
                return paperQuestion.getQuestionScore();
            } else {
                if (MultiScoreWayEnum.MISSED_SCORE_SET.getCode().equals(paperQuestion.getMultiScoreWay())) {
                    // 设置漏选得分
                    return paperQuestion.getQuestionScore();
                } else {
                    // 每个正确选项得分
                    BigDecimal userScore = paperQuestion.getQuestionScore().multiply(new BigDecimal(userAnswerList.size()));
                    return userScore.compareTo(paperQuestion.getQuestionScore()) > 0 ? paperQuestion.getQuestionScore() : userScore;
                }
            }
        }

        // 用户答案存在错误选项
        return BigDecimal.ZERO;
    }

    /**
     * 获取考试历史最高分
     *
     * @param examUserRecord 考试记录
     * @return 历史最高分
     */
    public BigDecimal getHighestScore(ExamUserRecord examUserRecord) {
        LambdaQueryWrapper<ExamUserRecord> wrapper = new LambdaQueryWrapper<ExamUserRecord>()
                .eq(ExamUserRecord::getUserId, examUserRecord.getUserId())
                .eq(ExamUserRecord::getExamType, examUserRecord.getExamType())
                .eq(ExamUserRecord::getRelateId, examUserRecord.getRelateId())
                .orderByDesc(ExamUserRecord::getUserScore)
                .last(SqlUtil.limitOne());
        return examUserRecordDao.getOneOpt(wrapper).map(ExamUserRecord::getUserScore).orElse(BigDecimal.ZERO);
    }

    /**
     * 获取考试答案显示规则
     *
     * @param examUserRecord 考试记录
     * @return 考试答案显示规则
     */
    public ExamShowRule getShowAnswerRule(ExamUserRecord examUserRecord) {
        if (ExamTypeEnum.MOCK.getCode().equals(examUserRecord.getExamType())) {
            Exam exam = examDao.getByIdForCache(examUserRecord.getRelateId());
            if (ObjectUtil.isNotNull(exam)) {
                return BeanUtil.copyProperties(exam, ExamShowRule.class);
            }
        } else if (ExamTypeEnum.EXAMINATION.getCode().equals(examUserRecord.getExamType())) {
            Exam exam = examDao.getByIdForCache(examUserRecord.getRelateId());
            if (ObjectUtil.isNotNull(exam)) {
                return BeanUtil.copyProperties(exam, ExamShowRule.class);
            }
        }
        return null;
    }

    /**
     * 获取考试信息
     *
     * @param examUserRecord 考试记录
     * @return 考试信息
     */
    public Exam getExam(ExamUserRecord examUserRecord) {
        if (ExamTypeEnum.MOCK.getCode().equals(examUserRecord.getExamType())) {
            return examDao.getByIdForCache(examUserRecord.getRelateId());
        } else if (ExamTypeEnum.EXAMINATION.getCode().equals(examUserRecord.getExamType())) {
            return examDao.getByIdForCache(examUserRecord.getRelateId());
        }
        return null;
    }

    /**
     * 获取考试名称
     *
     * @param examType 考试类型
     * @param relateId 关联ID
     * @return 考试名称
     */
    public String getExamName(Integer examType, Long relateId) {
        if (ExamTypeEnum.MOCK.getCode().equals(examType)) {
            Exam exam = examDao.getByIdForCache(relateId);
            return ObjectUtil.isNotNull(exam) ? exam.getExamName() : "";
        } else if (ExamTypeEnum.EXAMINATION.getCode().equals(examType)) {
            Exam exam = examDao.getByIdForCache(relateId);
            return ObjectUtil.isNotNull(exam) ? exam.getExamName() : "";
        }
        return "";
    }

    /**
     * 检查用户是否可以继续考试
     *
     * @param userId   用户ID
     * @param examType 考试类型
     * @param examId   考试ID
     * @return 是否可以继续考试
     */
    public ExamCheckContinue checkCanContinueExam(Long userId, Integer examType, Long examId) {
        // 统计已考次数
        Long userExamCount = examUserRecordDao.lambdaQuery()
                .eq(ExamUserRecord::getUserId, userId)
                .eq(ExamUserRecord::getExamType, examType)
                .eq(ExamUserRecord::getRelateId, examId)
                .count();

        if (ExamTypeEnum.EXAMINATION.getCode().equals(examType)) {
            // 正式考试，只能考一次
            return new ExamCheckContinue(userExamCount < 1, userExamCount, 0L);
        } else {
            // 模拟考试，课时考试
            Exam exam = examDao.getById(examId);
            if (ObjectUtil.isNull(exam)) {
                // 考试不存在，不允许继续考试
                return new ExamCheckContinue(Boolean.FALSE, 0L, 0L);
            }

            if (exam.getExamCount() > 0) {
                long residueExamCount = exam.getExamCount() - userExamCount;

                return new ExamCheckContinue(residueExamCount > 0, userExamCount, Math.max(residueExamCount, 0L));
            } else {
                return new ExamCheckContinue(Boolean.TRUE, userExamCount, -1L);
            }
        }
    }

    @Accessors(chain = true)
    @Data
    public static class ExamShowRule implements Serializable {

        @Serial
        private static final long serialVersionUID = -4319271544161680324L;

        /**
         * 成绩显示(0：禁止、1：显示)
         */
        private Boolean resultShow;

        /**
         * 成绩展示规则(1：交卷后、2：考试结束后、3：自定义)-只能查看评阅完成的记录
         */
        private Integer resultShowRule;

        /**
         * 成绩展示时间
         */
        private LocalDateTime resultShowTime;

        /**
         * 作答内容展示(0：禁止、1：显示)-开启成绩显示才能开启
         */
        private Boolean answerShow;

        /**
         * 作答内容展示规则(1：查看对错和答案解析、2：只看对错)-只能查看评阅完成的记录
         */
        private Integer answerShowRule;

        /**
         * 结果显示名次(0：禁止、1：显示)-开启成绩显示才能开启
         */
        private Boolean resultShowRank;

        /**
         * 排行榜(0：禁止、1：显示)
         */
        private Boolean rankList;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class ExamCheckContinue implements Serializable {

        @Serial
        private static final long serialVersionUID = 3261644909762416075L;

        /**
         * 是否能继续考试
         */
        private Boolean startExam;

        /**
         * 用户考试次数
         */
        private Long userExamCount;

        /**
         * 剩余考试次数，-1：表示无限次
         */
        private Long residueExamCount;

    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class ExamEvaluationResult implements Serializable {

        @Serial
        private static final long serialVersionUID = 153426560487796500L;

        /**
         * 题目数量
         */
        private Integer questionCount;

        /**
         * 正确题目数量
         */
        private Integer correctQuestionCount;

        /**
         * 用户得分
         */
        private BigDecimal userScore;

        /**
         * 主观题得分
         */
        private BigDecimal subjectiveQuestionScore;

        /**
         * 客观题得分
         */
        private BigDecimal objectiveQuestionScore;

        /**
         * 待更新用户答案集合
         */
        private List<ExamUserAnswer> updateAnswerList;
    }
}
